﻿#include "filecontainer.h"
#include "../_global.h"
#include "resmanager.h"

#ifdef _DEBUG
//#ifdef _CRTDBG_MAP_ALLOC
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
//#endif 
#endif 

void FileContainer::init(){
 	setID(_global.incAndGetCurrentMaxID());
	fileName=L"";
	fileSize=0;
	fileCurPosition=0;
	containerType=CT_INVALID;
	containerData=NULL;
	//version for load and parse
	containerVersion=-1;
	//version for save (allways maximum version)
	containerVersionForSave=1;
	containerFlags=0;
	headerSize=sizeof(containerSize)+sizeof(containerType)+sizeof(ID);
	dataSize=0;
	containerSize=getContainerSize();
	//calcContainerSize();
}

FileContainer::FileContainer(){
  init();
}

FileContainer::FileContainer(ContainerType ct){
  init();
	setContainerType(ct);
}

FileContainer::~FileContainer(){
  if(containerData!=NULL){
		delete [] containerData;
		containerData=NULL;
	}
}
/*
void FileContainer::calcContainerSize(){
	if(containerType==CT_CONTAINER){
		dataSize=sizeof(containerVersion)
				    +sizeof(containerFlags)
					  +4
					  +(containerCaption.size());
		vector<SPtr<FileContainer>>::iterator iter;
	  for(iter=childs.begin();iter!=childs.end();iter++){
		  if((*iter).p()->getContainerSize()>0)
			  dataSize+=(*iter).p()->getContainerSize();
	  }
	}
	containerSize=headerSize+dataSize;
}
*/
int FileContainer::load(){

	if(fileName.compare(L"")==0)return -1;
	
  FILE *stream=NULL;
	errno_t err=_wfopen_s(&stream,fileName.c_str(),L"rb");
	if(stream==NULL)return -1;
	if(err!=0){
    fclose(stream);
		return -2;
	}
	int fd=_fileno(stream);
	struct stat buf;
  fstat(fd, &buf);
  fileSize = buf.st_size;
	fileCurPosition=0;

	parse(stream,fileCurPosition,fileSize);

	fclose(stream);

  return 0;

}


int FileContainer::save(){

	if(getID()==0)return 0;
	if(getID()<0)return -3;
	if(containerType==CT_INVALID)return 0;
	if(fileName.compare(L"")==0)return -1;
	if(containerFlags & FC_FLAG_READ_ONLY>0)return 0;

	FILE *stream=NULL;
	errno_t err=_wfopen_s(&stream,fileName.c_str(),L"wb");
	if(stream==NULL)return -4;
	if(err!=0){
    fclose(stream);
		return -2;
	}

	fileCurPosition=0;

	saveThis(stream);
	/*if(childs.size()>0){
		vector<SPtr<FileContainer>>::iterator iter;
		for(iter=childs.begin();iter!=childs.end();iter++){
			(*iter).p()->saveThis(stream);
		}
	}*/

	fclose(stream);

  return 0;

}

void FileContainer::setContainerCaption(wchar_t *_caption){
	if(_caption==NULL)return;
  wstring str(_caption);
	setContainerCaption(str);
}

void FileContainer::setContainerCaption(wstring _caption){
  containerCaption=_caption;
}

wstring FileContainer::getContainerCaption(){
  return containerCaption;
}

int FileContainer::setFileName(wstring &_fileName){
	fileName=_fileName;
	return 0;
}

int FileContainer::setFileName(wchar_t *_fileName){
	if(_fileName==NULL)return 0;
  fileName=_fileName;
	return 0;
}

int FileContainer::saveThis(FILE *_stream){
	if(_stream==NULL)return -1;

	//long headerSize=sizeof(containerType)+sizeof(ID)+sizeof(containerSize);
	//calcContainerSize();
	//long dataSize=containerSize-headerSize;

	containerSize=getContainerSize();

	if(containerType<0 ||	getID()<0 || containerSize<headerSize){
	  return -2;
	}

	//if(containerType!=CT_CONTAINER && containerData==NULL)return -3;
	if(dataSize<0)return -4;

  __int64 id=getID();

	long tmp1;

	//Save version always is highest
	//containerVersion - version from loading (not save). ==-1 if no loading procedure for container
	switch(containerType){
	case CT_CONTAINER:
		  /*dataSize=0;
			if(childs.size()>0){
				vector<SPtr<FileContainer>>::iterator iter;
				for(iter=childs.begin();iter!=childs.end();iter++){
					if((*iter).p()->getContainerSize()<0)return -4;
					dataSize+=(*iter).p()->getContainerSize();
				}
			}*/
			/*containerSize=dataSize+headerSize
				  +sizeof(containerVersion)
					+sizeof(containerFlags)
					+4
					+(containerCaption.size()+2);*/
			if(fwrite((const void *)&containerSize,sizeof(containerSize),1,_stream)!=1)return -5;
			if(fwrite((const void *)&containerType,sizeof(containerType),1,_stream)!=1)return -5;
			if(fwrite((const void *)&id,sizeof(id),1,_stream)!=1){
				setID(id);
				return -5;
			}
			//Version for save:
			if(fwrite((const void *)&containerVersionForSave,sizeof(containerVersionForSave),1,_stream)!=1)return -5;
			//Version for load = save
			containerVersion=containerVersionForSave;
			if(fwrite((const void *)&containerFlags,sizeof(containerFlags),1,_stream)!=1)return -5;
			tmp1=containerCaption.size();
			if(fwrite((const void *)&tmp1,sizeof(tmp1),1,_stream)!=1)return -5;
			if(tmp1>0){
        if(fwrite((const void *)containerCaption.data(),2*containerCaption.size(),1,_stream)!=1)return -5;
			}
			if(childs.size()>0){
				vector<SPtr<FileContainer>>::iterator iter;
				for(iter=childs.begin();iter!=childs.end();iter++){
					if((*iter).p()->getContainerSize()<0)return -4;
					(*iter).p()->saveThis(_stream);
				}
			}
		break;
	default: 
			if(fwrite((const void *)&containerSize,sizeof(containerSize),1,_stream)!=1)return -5;
			if(fwrite((const void *)&containerType,sizeof(containerType),1,_stream)!=1)return -5;
			if(fwrite((const void *)&id,sizeof(id),1,_stream)!=1){
				//setID(id);
				return -5;
			}
			if(containerData!=NULL)if(fwrite((const void *)containerData,1,dataSize,_stream)!=dataSize)return -5;
		break;
  }

	return 0;

}

void FileContainer::setContainerType(ContainerType ct){
  containerType=ct;
	setDataCopy();
}

long FileContainer::getContainerSize(){
	/*if(containerType==CT_CONTAINER){
		containerSize=headerSize;
		vector<SPtr<FileContainer>>::iterator iter;
	  for(iter=childs.begin();iter!=childs.end();iter++){
		  //if((*iter).p()->getContainerSize()>0)
				containerSize+=(*iter).p()->getContainerSize();
	  }
	}
	else{*/
	  containerSize=headerSize+getDataSize();
	/*}*/
  return containerSize;
}

long FileContainer::getDataSize(){
	if(containerType==CT_CONTAINER){
		dataSize=sizeof(containerVersion)
				    +sizeof(containerFlags)
					  +4/*num of containerCaption wchars*/
					  +(containerCaption.size());
		vector<SPtr<FileContainer>>::iterator iter;
	  for(iter=childs.begin();iter!=childs.end();iter++){
		  //if((*iter).p()->getContainerSize()>0)
				dataSize+=headerSize+(*iter).p()->getDataSize();
	  }
	}
	return dataSize;
	//if(containerSize-containerSize<0)return 0;
  //return containerSize-headerSize;
}


int FileContainer::setDataCopy(char *inData,long _size){
	
	if(_size<0)return -2;
	//if(inData==NULL)return -3;
	if(containerType==CT_CONTAINER || containerType==CT_INVALID)return -3;

	dataSize=_size;
	if(_size==0){
		if(containerData!=NULL){
			delete [] containerData;
			containerData=NULL;
		}
		containerSize=headerSize;
		return 0;
	}

	if(containerData!=NULL){
	  delete [] containerData;
	  containerData=NULL;
	}

	containerData=new char[_size+1];
	containerSize=headerSize+dataSize;

	memcpy((void *)containerData,(void *)inData,_size);
	//for(long i=0;i<_size;i++){
  //  containerData[i]=inData[i];
	//}

	return 0;

}

long FileContainer::parse(FILE *_stream,long _fileCurPosition,long _fileSize,long _containerVersion){

	fileCurPosition=_fileCurPosition;

  long nextContainerPosition=fileCurPosition;

	containerVersion=_containerVersion;

	if(nextContainerPosition+(long)sizeof(containerSize)>_fileSize)return -2;
	int a;
	if((a=fread((void *)&containerSize,sizeof(containerSize),1,_stream ))!=1){
		 containerType=CT_INVALID;
		 setID(-1);
		 return -1;
	}
	nextContainerPosition+=sizeof(containerSize);

	long tmpType;

	if(nextContainerPosition+(long)sizeof(tmpType)>_fileSize)return -2;
	if(fread((void *)&tmpType,sizeof(tmpType),1,_stream )!=1){
		 containerType=CT_INVALID;
		 setID(-1);
		 return -1;
	}
	containerType=ContainerType(tmpType);
	nextContainerPosition+=sizeof(containerType);
  __int64 id;
	if(nextContainerPosition+(long)sizeof(ID)>_fileSize)return -2;
	if(fread((void *)&id,sizeof(id),1,_stream )==1){
		setID(id);
	}
	else{
		 containerType=CT_INVALID;
		 setID(-1);
		 return -1;
	}
	nextContainerPosition+=sizeof(ID);

	if(containerData!=NULL){
		delete [] containerData;
		containerData=NULL;
	}

  if(fileCurPosition+containerSize>_fileSize)return -2;

	//long headerSize=nextContainerPosition-_fileCurPosition;
	dataSize=containerSize-headerSize;

	childs.clear();

	long res=1;
  long containerCaptionLen;  
	wchar_t *wbuf=NULL;

	switch(containerType){
		case CT_CONTAINER:
			while(res>0){
				if(nextContainerPosition+(long)sizeof(containerVersion)>_fileSize)return -2;
	      if(fread((void *)&containerVersion,sizeof(containerVersion),1,_stream)!=1){
		      containerVersion=-1;
					containerType=CT_INVALID;
		      setID(-1);
		      return -1;
	      }
				nextContainerPosition+=sizeof(containerVersion);
				if(nextContainerPosition+(long)sizeof(containerFlags)>_fileSize)return -2;
	      if(fread((void *)&containerFlags,sizeof(containerFlags),1,_stream )!=1){
		      containerVersion=-1;
					containerType=CT_INVALID;
		      setID(-1);
		      return -1;
	      }
				nextContainerPosition+=sizeof(containerFlags);
				if(nextContainerPosition+(long)sizeof(containerCaptionLen)>_fileSize)return -2;
	      if(fread((void *)&containerCaptionLen,sizeof(containerCaptionLen),1,_stream )!=1){
		      containerVersion=-1;
					containerType=CT_INVALID;
		      setID(-1);
		      return -1;
	      }
				nextContainerPosition+=sizeof(containerCaptionLen);
				if(containerCaptionLen<0 && containerCaptionLen>10000){
		      containerVersion=-1;
					containerType=CT_INVALID;
		      setID(-1);
		      return -1;
	      }
        nextContainerPosition+=containerCaptionLen*2;
			
				wbuf=new wchar_t[containerCaptionLen+1];
				if(nextContainerPosition+containerCaptionLen>_fileSize){
          delete [] wbuf;
					wbuf=NULL;
					return -2;
				}
				if(containerCaptionLen>0){
	        if(fread((void *)wbuf,containerCaptionLen*2,1,_stream)!=1){
		        containerVersion=-1;
					  containerType=CT_INVALID;
		        setID(-1);
					  delete [] wbuf;
					  wbuf=NULL;
		        return -1;
	        }
				}
				wbuf[containerCaptionLen]='\0';
				containerCaption=wbuf;
				delete [] wbuf;
				wbuf=NULL;

				while(nextContainerPosition<fileCurPosition+containerSize){// _fileSize){
				  SPtr<FileContainer> fc;
					//fc=new SPtr<FileContainer>();
				  res=fc.p()->parse(_stream,nextContainerPosition,_fileSize,containerVersion);
					if(!(res<0 || fc.p()->getContainerSize()<=0)){
				    childs.push_back(fc);
				    nextContainerPosition+=fc.p()->getContainerSize();
					}
					//delete fc;
				}
			}
			break;
		default: 
			if(fileCurPosition+containerSize>_fileSize)return -2;
			if(dataSize<=0)return 0;
			if(containerData!=NULL){
				delete [] containerData;
				containerData=NULL;
			}
			containerData=new char[dataSize+1];
			if(nextContainerPosition+dataSize>_fileSize){
				return -2;
			}
			if(fread((void *)containerData,dataSize,1,_stream)!=1){
    		containerType=CT_INVALID;
				setID(-1);
				return -1;
			}
			nextContainerPosition+=dataSize;
			//return -1;
			break;
	}

	return 0;

}


FileContainer *FileContainer::getContainerAtID(__int64 id){
	
	if(getID()==id)return this;
	vector<SPtr<FileContainer>>::iterator iter;
	for(iter=childs.begin();iter!=childs.end();iter++){
		if((*iter).p()->getContainerAtID(id)!=NULL)return (*iter).p();
	}
	return NULL;

}


ContainerType FileContainer::getContainerType(){
  return containerType;
}

long FileContainer::getContainerVersion(){
  return containerVersion;
}

long FileContainer::getContainerVersionForSave(){
  return containerVersionForSave;
}

char *FileContainer::getContainerData(){
  return containerData;
}

vector<SPtr<FileContainer>> *FileContainer::getChilds(){
  return &childs;
}

unsigned long FileContainer::getContainerFlags(){
  return containerFlags;
}

int FileContainer::addFileContainerToChilds(SPtr<FileContainer> &_fc){
	childs.push_back(_fc);
	return 0;
}